有許多電商界的朋友都曾經向我詢問過一個需求,他們想要抓到粉絲頁的按讚或粉絲名單,但是可惜的,FB 不給的你不能要。不過他們都會再進階的問一個問題,能否抓到文章點讚的名單?基本上 FB graph api 是只允許抓自己粉絲頁的,但對爬蟲而言卻是「所見即所得」,今天我們的主題就來挑戰一下抓取 FB 某篇文章的按讚名單
這次我們來使用這篇文章來做,同時公關一下,這家草莓大福很有名,預約都排到三個月以後了。
首先進入網址後會看到這篇文章的主體,在文章下面,有按讚的總人數,點擊這個數字之後,我們就可以看到按讚的名單。同時我們發現,當我們點擊的時候,送出了一個 get request,裡面包含了許多 query 參數。在 response 裡面,我們若全部展開,會發現這個 request 將按讚名單的 html 夾在 jsmods
的 markup
裡面,在這個 html 除了包含按讚的名單外,也包含了下一個分頁的連結,也就是說,若我們能夠模擬出這個 request,那我們就能順利取得名單。
再來我們發現這個 request 其實只有第一頁,名單捲到最底部有一個「更多」的按鈕,按下去之後會讀取下一個分頁的名單。同時我們也發現,點擊「更多」之後,送出了一個 get request,裡面也包含了許多 query 參數,同時也在 response 裡面找到下一個分頁的按讚名單,不過這邊發現,資料的位置跟第一個 request 不同,這次的資料是放在 domops
裡面,名單的 html 放在 domops[0]
裡面,而再下一頁的連結放在 domops[1]
裡面。
若我們能搞定這兩個 request,就能取得這篇文章的按讚名單了。
要取得所有按讚名單,那麼會拆解成以下四個步驟:
首先我們模擬一下這個 request,記得帶上 cookie
和 user-agent
,確定是可以順利取得 response。
接下來我們將 response 的 json 擷取下來,並且把前面的贅字拿掉,再用 chrome 解析一下 json,然後將目標 html 建立一個網頁來觀察一下,確定是可以 select 到所有的名單,再確認一下下一個分頁的 url,也是可以拿到的。
將上面取的 url 輸入 postman 試試看,結果發現並沒有辦法順利取得數值,兩相比對一下 network 裡面的網址,發現我們取得的下一頁網址少了些參數。經過測試之後,發現只要沒有 ft_ent_identifier
和 __a
就不會成功,所以我們必須手動加上 ft_ent_identifier
和 __a
,這樣就能順利的得 response 了。
我們一樣把 response 放到空的 html 去解析,確定可以 select 到所有的名單。再把更多分頁的 html 也解析,一樣能夠順利拿到 url,那整個過程應該就沒問題了。
我們先來實作發出第一個 request 的 getActionList function,收一個 url 當作參數,然後將按讚名單傳給 callback。這邊比較要注意的是,因為我們取得更多連結的時候,要手動將 ft_ent_identifier
和 __a
帶上,所以這邊我們會先去 pasre 一下參數傳進來的 url 中的 ft_ent_identifier
,並在我們取得「更多」按鈕連結的時候將其加上,而若有「更多」的按鈕我們則會去 call getMoreList function,這個 function 我們等下會實作。
const parse = require('url-parse');
function getActionList(url , callback){
var ft_ent_identifier = parse(url, true).query.ft_ent_identifier
request(url, (err, res, body)=>{
var data = JSON.parse(body.replace('for (;;);', ''))
var html = data.jsmods.markup[0][1].__html
var $ = cheerio.load(html)
var list = $('._5j0e a').map((index, obj)=>{
return {
id: JSON.parse($(obj).attr('data-gt')).engagement.eng_tid,
name: $(obj).text(),
link: $(obj).attr('href'),
}
}).get()
if($('.uiMorePager').length){
var url = 'https://www.facebook.com' + $('.uiMorePager a').attr('href') + `&ft_ent_identifier=${ft_ent_identifier}&__a=1`
getMoreList(url, (moreList)=>{
callback(list.concat(moreList))
})
}else{
callback(list)
}
})
}
接下來實作取得更多連結的 request,這邊要注意的是 response 的名單和「更多」的按鈕在不同的位置。而若有「更多」的按鈕,那我們就遞迴呼叫 getMoreList 自己。
function getMoreList(url, callback){
var ft_ent_identifier = parse(url, true).query.ft_ent_identifier
request(url, (err, res, body)=>{
var data = JSON.parse(body.replace('for (;;);', ''))
var html = data.domops[0][3].__html;
var $ = cheerio.load(html)
var list = $('._5j0e a').map((index, obj)=>{
return {
id: JSON.parse($(obj).attr('data-gt')).engagement.eng_tid,
name: $(obj).text(),
link: $(obj).attr('href'),
}
}).get()
if(data.domops[1][3]){
var html = data.domops[1][3].__html;
var $ = cheerio.load(html)
var url = 'https://www.facebook.com' + $('.uiMorePager a').attr('href') + `&ft_ent_identifier=${ft_ent_identifier}&__a=1`
getMoreList(url, (moreList)=>{
callback(list.concat(moreList))
})
}else{
callback(list)
}
})
}
最後我們要將按讚數字的那個連結傳給 getActionList function,這樣就OK了。
var url = 'https://www.facebook.com/ufi/reaction/profile/dialog/?ft_ent_identifier=2051019961809577&av=1402351764&dpr=2&__asyncDialog=2&__user=1402351764&__a=1&__dyn=5V4cjLx2ByK5A9UkKHqAyqomzFEbFbGAdyemt94WqF1eq7UnGdwIhEpyEybGqK6qxeqaxu2GcyWDyubnyogyEnGi4FpeuUzxeAcU8UqDodEHzoK26aAUg-nDLzA5KcyF8O49ElwQUlByECii8yEqx6exu2m5HxirVe48G6EvG5FA5u59XgK5Ey4UlDBgS8x2i2eqaCzu5oV1yWLBx6695UCUZqBxeybaWzQQ25jUyq2mbLAAzUxa9BKazodotK8zoymf-Egy8Gdx69hEqAzULgSa-FpF-23Rh8Gmm8h63Cmh2ubJ2oC&__req=1l&__be=1&__pc=PHASED%3ADEFAULT&__rev=3544586&__spin_r=3544586&__spin_b=trunk&__spin_t=1513907702&ft[tn]=]%2BZ*F&ft[type]=44';
getActionList(url, (list)=>{
console.log(list);
})
const request = require('request').defaults({
headers: {
'cookie': 'xxxxxxxxxx',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
}
});
const cheerio = require('cheerio');
const async = require('async');
const parse = require('url-parse');
var url = 'https://www.facebook.com/ufi/reaction/profile/dialog/?ft_ent_identifier=2051019961809577&av=1402351764&dpr=2&__asyncDialog=2&__user=1402351764&__a=1&__dyn=5V4cjLx2ByK5A9UkKHqAyqomzFEbFbGAdyemt94WqF1eq7UnGdwIhEpyEybGqK6qxeqaxu2GcyWDyubnyogyEnGi4FpeuUzxeAcU8UqDodEHzoK26aAUg-nDLzA5KcyF8O49ElwQUlByECii8yEqx6exu2m5HxirVe48G6EvG5FA5u59XgK5Ey4UlDBgS8x2i2eqaCzu5oV1yWLBx6695UCUZqBxeybaWzQQ25jUyq2mbLAAzUxa9BKazodotK8zoymf-Egy8Gdx69hEqAzULgSa-FpF-23Rh8Gmm8h63Cmh2ubJ2oC&__req=1l&__be=1&__pc=PHASED%3ADEFAULT&__rev=3544586&__spin_r=3544586&__spin_b=trunk&__spin_t=1513907702&ft[tn]=]%2BZ*F&ft[type]=44';
getActionList(url, (list)=>{
console.log(list);
})
function getActionList(url , callback){
var ft_ent_identifier = parse(url, true).query.ft_ent_identifier
request(url, (err, res, body)=>{
var data = JSON.parse(body.replace('for (;;);', ''))
var html = data.jsmods.markup[0][1].__html
var $ = cheerio.load(html)
var list = $('._5j0e a').map((index, obj)=>{
return {
id: JSON.parse($(obj).attr('data-gt')).engagement.eng_tid,
name: $(obj).text(),
link: $(obj).attr('href'),
}
}).get()
if($('.uiMorePager').length){
var url = 'https://www.facebook.com' + $('.uiMorePager a').attr('href') + `&ft_ent_identifier=${ft_ent_identifier}&__a=1`
getMoreList(url, (moreList)=>{
callback(list.concat(moreList))
})
}else{
callback(list)
}
})
}
function getMoreList(url, callback){
var ft_ent_identifier = parse(url, true).query.ft_ent_identifier
request(url, (err, res, body)=>{
var data = JSON.parse(body.replace('for (;;);', ''))
var html = data.domops[0][3].__html;
var $ = cheerio.load(html)
var list = $('._5j0e a').map((index, obj)=>{
return {
id: JSON.parse($(obj).attr('data-gt')).engagement.eng_tid,
name: $(obj).text(),
link: $(obj).attr('href'),
}
}).get()
if(data.domops[1][3]){
var html = data.domops[1][3].__html;
var $ = cheerio.load(html)
var url = 'https://www.facebook.com' + $('.uiMorePager a').attr('href') + `&ft_ent_identifier=${ft_ent_identifier}&__a=1`
getMoreList(url, (moreList)=>{
callback(list.concat(moreList))
})
}else{
callback(list)
}
})
}
老實說這種爬蟲對電商圈的朋友來說還挺有價值的,而通常懂電商的人不會懂這些技術,懂這些技術的人不會懂資料的價值,我一直期望能夠辦個黑客松主題是「電商x技術」,由電商圈的朋友們來許願,然後由技術夥伴在週末完成,我想這個會非常有價值。